package com.common.aspect;

import com.common.ano.Log;
import com.common.client.AopClient;
import com.common.dto.OperationLogDTO;
import com.common.service.AopService;
import com.common.utils.JwtUtil;
import com.common.utils.RequestContextUtil;
import com.common.vo.RegisterVO;
import com.common.vo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.time.LocalDateTime;


@Component
@Slf4j
@Aspect
public class LogAspect {
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private AopClient aopClient;
    @Resource
    private AopService aopService;

    //设置切入点
    @Pointcut("@annotation(com.common.ano.Log)")
    public void pointCut(){}

    /**
     * 环绕通知：在方法执行前后记录日志
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 创建日志对象
        OperationLogDTO operationLog = new OperationLogDTO();

        try {
            // 1. 获取HttpServletRequest
            ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes != null ? attributes.getRequest() : null;

            // 2. 获取方法签名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            // 3. 获取@Log注解的信息
            Log logAnnotation = method.getAnnotation(Log.class);
            if (logAnnotation != null) {
                operationLog.setOperationType(logAnnotation.operationType());
                operationLog.setOperationModule(logAnnotation.operationModule());
                operationLog.setOperationDesc(logAnnotation.operationDesc());
            }

            // 4. 获取请求信息
            if (request != null) {
                operationLog.setRequestMethod(request.getMethod());
                operationLog.setRequestUrl(request.getRequestURI());

                // 获取请求参数（JSON格式）
                Object[] args = joinPoint.getArgs();
                if (args != null && args.length > 0) {
                    try {
//                        好的想法，但不正确
//                        if(args[0] instanceof LoginFormDTO dto){
//                            String username = dto.getUsername();
//                            operationLog.setUserName(username);
//                            Long userId = authClient.getByUsername(username);
//                            if(userId != null){
//                                operationLog.setUserId(userId);
//                            }
//                        }
                        // 过滤掉HttpServletRequest/Response等不需要序列化的参数
                        Object[] filteredArgs = java.util.Arrays.stream(args)
                                .filter(arg -> !(arg instanceof HttpServletRequest)
                                        && !(arg instanceof jakarta.servlet.http.HttpServletResponse))
                                .toArray();
                        if (filteredArgs.length > 0) {
                            String params = objectMapper.writeValueAsString(filteredArgs.length == 1 ? filteredArgs[0] : filteredArgs);
                            operationLog.setRequestParams(params);
                        }
                    } catch (Exception e) {
                        log.warn("参数序列化失败: {}", e.getMessage());
                        operationLog.setRequestParams("参数序列化失败");
                    }
                }
            }

            // 5. 获取用户信息（从请求头或ThreadLocal中获取）
            Long userId = RequestContextUtil.getCurrentUserId();
            if(userId != null){
                operationLog.setUserId(userId);
            }
            String userName = RequestContextUtil.getCurrentUsername();
            if(userName != null){
                operationLog.setUserName(userName);
            }

            // 6. 执行目标方法
            Object result = joinPoint.proceed();

            // 7. 记录成功状态
            operationLog.setStatus("0");
            operationLog.setOperationTime(LocalDateTime.now());
            operationLog.setCreateTime(LocalDateTime.now());

            //注册操作
            if (result instanceof Result<?> r && r.getData() instanceof RegisterVO registerVO) {
                operationLog.setUserId(registerVO.getUserId());
                operationLog.setUserName(registerVO.getUsername());
            }

            //登录操作
            if (result instanceof Result<?> r && r.getData() instanceof String token) {
                Claims claims = JwtUtil.parseJwt(token);
                Long userId1 = claims.get("userId", Long.class);
                String username = claims.get("username", String.class);
                operationLog.setUserId(userId1);
                operationLog.setUserName(username);
            }

            return result;

        } catch (Throwable e) {
            // 8. 记录失败状态
            operationLog.setStatus("1");
            operationLog.setErrorMsg(e.getMessage());
            operationLog.setOperationTime(LocalDateTime.now());
            operationLog.setCreateTime(LocalDateTime.now());

            log.error("方法执行异常: {}", e.getMessage(), e);
            throw e;

        } finally {
            // 9. 异步保存日志到数据库（避免影响主业务）
            try {
                long costTime = System.currentTimeMillis() - startTime;
                operationLog.setCostTime(costTime);
                //异步执行
                aopService.saveLog(operationLog);
            } catch (Exception e) {
                log.error("保存操作日志失败: {}", e.getMessage(), e);
            }
        }
    }
}
